package ch.trackedbean.binding.action;

import javax.swing.*;
import ch.trackedbean.binding.action.MenuPath.MenuPathSegment;

/**
 * Object representing an action entry.
 * 
 * @param <T> The action type
 * @author M. Hautle
 */
public class ActionEntry<T extends Action> implements Comparable<ActionEntry> {
    /** The path to the action. */
    private final MenuPath path;

    /** The order of the action. Lower values come first. */
    private final int order;

    /** The action. */
    private final T action;

    /**
     * Default constructor.
     * 
     * @param path The path to the action
     * @param order The order value of the action (used to sort them, lower values come first)
     * @param action The action
     */
    public ActionEntry(MenuPath path, int order, T action) {
        this.path = path;
        this.order = order;
        this.action = action;
    }

    /**
     * Return the path of this action
     * 
     * @return The path
     */
    public MenuPath getPath() {
        return path;
    }

    /**
     * Returns the action.
     * 
     * @return The action
     */
    public T getAction() {
        return action;
    }

    /**
     * Returns the order value of the action.
     * 
     * @return The order value
     */
    public int getOrder() {
        return order;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int compareTo(ActionEntry o) {
        final MenuPathSegment[] other = o.path.getPath();
        final MenuPathSegment[] menuPath = path.getPath();
        final int cnt = Math.min(menuPath.length, other.length);
        // compare submenus
        for (int i = 0; i < cnt; i++) {
            // check the groups
            final int grp = menuPath[i].compareGroups(other[i]);
            if (grp != 0)
                return grp;
            // subMenu is guaranteed to be non null
            final String s1 = menuPath[i].getSubMenu();
            final String s2 = other[i].getSubMenu();
            // check text
            final int res = s1.compareTo(s2);
            // same menu text
            if (res == 0)
                continue;
            // compare order values
            final int o1 = menuPath[i].getSubMenuOrder();
            final int o2 = other[i].getSubMenuOrder();
            if (o1 < o2)
                return -1;
            if (o1 > o2)
                return 1;
            // same order value - the text difference matters
            return res;
        }
        // same menu
        if (menuPath.length == other.length)
            return compareActions(o);
        // our action 'against' a submenu
        if (menuPath.length == cnt)
            return comparePathToAction(o, this) * -1;
        // our submenu 'against' a action
        return comparePathToAction(this, o);
    }

    /**
     * Compares the last common part of two paths.<br>
     * The action of the second entry will be compared against the submenu (at the same location) of the first entry.
     * 
     * @param o1 The longer entry (were the submenu will be taken from)
     * @param o2 The shorter entry (were the action will be taken from)
     * @return 0,1,-1 if the first entry is equals, 'bigger' or 'lower' than the second entry
     */
    private int comparePathToAction(ActionEntry o1, ActionEntry o2) {
        final MenuPathSegment segment = o1.path.getPath()[o2.path.getPath().length];
        final int grp = MenuPath.compareGroups(segment.getGroup(), segment.getGroupOrder(), o2.path.getLastGroup(), o2.path.getLastGroupOrder());
        if (grp != 0)
            return grp;
        return compare(segment.getSubMenu(), segment.getSubMenuOrder(), (String) o2.action.getValue(Action.NAME), o2.order);
    }

    /**
     * Compares the last group and the actions of two entries.
     * 
     * @param o The other entry
     * @return 0,1,-1 if the this entry is equals, 'bigger' or 'lower' than the other entry
     */
    private int compareActions(ActionEntry o) {
        final int grp = MenuPath.compareGroups(path.getLastGroup(), path.getLastGroupOrder(), o.path.getLastGroup(), o.path.getLastGroupOrder());
        if (grp != 0)
            return grp;
        // same group - compare the actions
        return compare((String) action.getValue(Action.NAME), order, (String) o.action.getValue(Action.NAME), o.order);
    }

    /**
     * Compares the given orders against each other and if their equals the string get compared.
     * 
     * @param s1 The first string
     * @param o1 The order of the first string
     * @param s2 The second string
     * @param o2 The order of the second string
     * @return -1, 0 or 1 as the first pair is less than, equal to or greater than the second pair
     */
    private int compare(String s1, int o1, String s2, int o2) {
        if (o1 < o2)
            return -1;
        if (o1 > o2)
            return 1;
        return s1.compareTo(s2);
    }
}